package com.craftworks.tiles;

import com.craftworks.tiles.util.ShadowRenderer;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

public class TileDashboard
{
    private final JLayeredPane layeredPane;
    private final JPanel tileBoard;
    private final ThumbnailPane thumbnailPane;

    private AbstractTile selectedTile;
    private int placeholderIndex;

    public TileDashboard()
    {
        layeredPane = new JLayeredPane();
        tileBoard = new JPanel(new TileLayout());
        thumbnailPane = new ThumbnailPane();

        layeredPane.add(tileBoard, JLayeredPane.DEFAULT_LAYER);
        layeredPane.add(thumbnailPane, JLayeredPane.DRAG_LAYER);

        configLayeredPane();
        configMouseActions();
    }

    public Component getView()
    {
        return layeredPane;
    }

    public void add(Tile tile)
    {
        tileBoard.add(tile.getView());
    }

    public void remove(Tile tile)
    {
        tileBoard.remove(tile.getView());
    }

    private void configLayeredPane()
    {
        layeredPane.addComponentListener(new ComponentAdapter()
        {
            @Override
            public void componentResized(ComponentEvent e)
            {
                tileBoard.setSize(layeredPane.getSize());
                thumbnailPane.setSize(layeredPane.getSize());
                layeredPane.revalidate();
            }
        });
        layeredPane.setPreferredSize(tileBoard.getPreferredSize());
        thumbnailPane.hideThumbnail();
    }

    private void configMouseActions()
    {
        tileBoard.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent e)
            {
                dragTile(e);
            }

            @Override
            public void mouseReleased(MouseEvent e)
            {
                dropTile(e);
            }
        });
        tileBoard.addMouseMotionListener(new MouseAdapter()
        {
            @Override
            public void mouseDragged(MouseEvent e)
            {
                tryLayoutTileManually(e);
            }
        });
    }

    private void dragTile(MouseEvent evt)
    {
        Component c = tileBoard.getComponentAt(evt.getPoint());
        if (c instanceof AbstractTile t)
        {
            // 创建快照
            BufferedImage thumbnail = new BufferedImage(t.getWidth(), t.getHeight(), BufferedImage.TYPE_INT_ARGB);
            t.paint(thumbnail.getGraphics());

            // 显示快照
            Point dragPoint = SwingUtilities.convertPoint(evt.getComponent(), evt.getPoint(), c);
            thumbnailPane.showThumbnail(thumbnail, evt.getLocationOnScreen(), dragPoint);

            // 移除Tile
            tileBoard.remove(c);

            selectedTile = t;
            evt.consume();
        }
    }

    private void dropTile(MouseEvent evt)
    {
        if (selectedTile != null)
        {
            thumbnailPane.hideThumbnail();
            if (placeholderIndex >= 0)
                tileBoard.add(selectedTile, placeholderIndex);
            else
                tileBoard.add(selectedTile); // 放在末尾

            selectedTile = null;
            placeholderIndex = -1;
            evt.consume();
        }
    }

    private void tryLayoutTileManually(MouseEvent evt)
    {
        if (selectedTile != null)
        {
            // 返回thumbnail（左上角）在屏幕上的坐标
            Point location = thumbnailPane.moveThumbnail(evt.getLocationOnScreen());
            SwingUtilities.convertPointFromScreen(location, layeredPane);

            // 以thumbnail左上角位置为布局参考点
            TileLayout layout = (TileLayout) tileBoard.getLayout();
            layout.layoutContainer(tileBoard);
            placeholderIndex = layout.addPlaceholderAt(tileBoard, selectedTile.getPreferredSize(), location);

            evt.consume();
        }
    }

    static class ThumbnailPane extends JComponent
    {
        private BufferedImage image;
        private Point pointerLocation;
        private Point dragPoint;
        private BufferedImage shadow;

        Point moveThumbnail(Point point)
        {
            Point oldPointerLocation = pointerLocation;
            SwingUtilities.convertPointFromScreen(point, this);
            pointerLocation = point;

            int imageWidth = image.getWidth();
            int imageHeight = image.getHeight();
            int shadowWidth = shadow.getWidth();
            int shadowHeight = shadow.getHeight();

            Rectangle newClip = new Rectangle();
            newClip.add(new Rectangle(pointerLocation.x - dragPoint.x, pointerLocation.y - dragPoint.y, imageWidth, imageHeight));
            newClip.add(new Rectangle(pointerLocation.x - dragPoint.x, pointerLocation.y - dragPoint.y, shadowWidth, shadowHeight));
            newClip.add(new Rectangle(oldPointerLocation.x - dragPoint.x, oldPointerLocation.y - dragPoint.y, imageWidth, imageHeight));
            newClip.add(new Rectangle(oldPointerLocation.x - dragPoint.x, oldPointerLocation.y - dragPoint.y, shadowWidth, shadowHeight));

            repaint(newClip);

            // 返回thumbnail左上角在屏幕上的坐标
            Point p = new Point(pointerLocation.x - dragPoint.x, pointerLocation.y - dragPoint.y);
            SwingUtilities.convertPointToScreen(p, this);
            return p;
        }

        void hideThumbnail()
        {
            setVisible(false);
        }

        void showThumbnail(BufferedImage thumbnail, Point locationInBoard, Point locationInThumbnail)
        {
            image = thumbnail;
            shadow = new ShadowRenderer(5, 0.3f, Color.BLACK).createShadow(thumbnail);

            SwingUtilities.convertPointFromScreen(locationInBoard, this);
            pointerLocation = locationInBoard;
            dragPoint = locationInThumbnail;
            setVisible(true);
        }

        @Override
        protected void paintComponent(Graphics g)
        {
            if (image != null && pointerLocation != null)
            {
                int x = pointerLocation.x - dragPoint.x;
                int y = pointerLocation.y - dragPoint.y;

                g.drawImage(shadow, x, y, null);
                g.drawImage(image, x, y, null);
            }
        }
    }
}
